package com.zys.background.auth.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zys
 * @since 2022-11-21
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("AlibabaClassNamingShouldBeCamel")
public class OAuth2EndpointUtils {
  public static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

  public static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
    Map<String, String[]> parameterMap = request.getParameterMap();
    MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
    parameterMap.forEach((key, values) -> {
      if (values.length > 0) {
        for (String value : values) {
          parameters.add(key, value);
        }
      }
    });
    return parameters;
  }

  static Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request, String... exclusions) {
    if (!matchesAuthorizationCodeGrantRequest(request)) {
      return Collections.emptyMap();
    }
    Map<String, Object> parameters = new HashMap<>(getParameters(request).toSingleValueMap());
    for (String exclusion : exclusions) {
      parameters.remove(exclusion);
    }
    return parameters;
  }

  static boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) {
    return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(
        request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) &&
        request.getParameter(OAuth2ParameterNames.CODE) != null;
  }

  static boolean matchesTokenRequest(HttpServletRequest request) {
    return matchesAuthorizationCodeGrantRequest(request) && request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
  }

  public static void throwError(String errorCode, String parameterName, String errorUri) {
    OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
    throw new OAuth2AuthenticationException(error);
  }
}
